package cn.icanci.rec.spi.event;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * 事件分发抽象处理器
 * 
 * @author icanci
 * @since 1.0 Created in 2022/11/11 18:01
 */
public abstract class AbstractEventDispatcher implements EventDispatcher, ApplicationContextAware {
    /**
     * Spring 容器
     */
    protected ApplicationContext                     applicationContext;

    /**
     * 事件监听器列表
     */
    protected Map<Class<?>, List<BaseEventListener>> eventListMap        = new ConcurrentHashMap<>();

    /**
     * 监听器
     */
    protected List<BaseEventListener>                listeners           = new LinkedList();

    /**
     * 注册事件类型
     */
    protected Set<Class<?>>                          eventClasses        = new HashSet<>();

    /**
     * 排序器
     */
    protected static final ListenerComparator        LISTENER_COMPARATOR = new ListenerComparator();

    /**
     * Asynchronous executor 
     */
    protected Executor                               taskExecutor;

    /**锁*/
    private final ReadWriteLock                      lock                = new ReentrantReadWriteLock();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        try {
            lock.writeLock().lock();
            register();
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 注册事件和监听器
     */
    protected abstract void register();

    /**
     * 获取事件执行线程池
     *
     * @return 事件执行线程池
     */
    protected abstract Executor getTaskExecutor();

    /**
     * 设置任务执行器
     *
     * @param taskExecutor 事件执行线程池
     */
    public abstract void setTaskExecutor(Executor taskExecutor);
}
